import hashlib
import time

from DrissionPage._pages.chromium_tab import ChromiumTab
from DrissionPage.common import Actions
from DrissionPage.errors import *

import business.crawling.chat_log as chat_log
import business.pre_crawling.check_chatgpt as check_chatgpt
import business.pre_crawling.load_chatgpt as load_chatgpt
import common.risk.cloudflare as cloudflare
import lib.utils.log_loader as log_loader
from business.crawling.chat_log import ChatQuestionAnswer
from common.data.element_selectors import PROMPT_TEXTAREA_SELECTOR, SEND_BUTTON_SELECTOR, LISTEN_STATE, \
    CHAT_RECORDS_LAST, CHAT_MESSAGE, CHAT_RECORDS_START, CHAT_MESSAGE_ANSWER, CHAT_RECORDS, ANSWER_MARKDOWN, WEB_CSS
from lib.utils.redis_util import get_redis_client


def text_chat(tab: ChromiumTab):
    """
    进行文本聊天
    :param tab:
    :return:
    """
    logger = log_loader.get_logger()
    logger.debug(f'监听聊天问题开始')
    redis_client = get_redis_client()

    tab.wait.load_start()

    # 创建动作对象，用于对页面元素进行操作
    ac = Actions(tab)

    redis_client.set(CHAT_MESSAGE, '')

    redis_client.set(LISTEN_STATE, 1)

    # 定义每次迭代的时间间隔（例如，5秒）
    interval = 2
    while True:
        # 监听控制状态
        listen_state = redis_client.get(LISTEN_STATE)
        if listen_state and listen_state.isdigit() and int(listen_state) == 0:
            logger.debug('聊天监听已停止')
            break

        # 检查是否需要重新加载页面
        if not check_chatgpt.check_chatgpt_interaction():
            # 主动刷新下页面
            tab.refresh()
            ret = load_chatgpt.get_page(tab)
            if ret:
                check_chatgpt.save_chatgpt_interaction()
                check_chatgpt.save_new_chat()
                tab.wait.load_start()

        # 从Redis中获取消息或命令
        message = redis_client.get(CHAT_MESSAGE)
        if message and message.strip():
            logger.info(f"获取到消息：{message}")
            try:
                # 清除文本框，避免发送重复消息
                prompt_textarea = tab.ele(PROMPT_TEXTAREA_SELECTOR)
                prompt_textarea.clear()
                ac.move_to(ele_or_loc=prompt_textarea)
                ac.click()
                # 如果有新消息，将其输入到聊天窗口
                prompt_textarea.input(message)

                # 模拟点击发送按钮
                send_button = tab.ele(SEND_BUTTON_SELECTOR)
                ac.move_to(ele_or_loc=send_button)
                ac.click()

                # 状态更新清理
                redis_client.set(CHAT_MESSAGE, '')
                check_chatgpt.save_chatgpt_interaction()
                check_chatgpt.update_chat_state()
            except ElementNotFoundError:
                cloudflare.checkbox_click(tab)  # 如果未找到元素，尝试绕过 Cloudflare
            except Exception as e:
                logger.error(f"错误查找元素: {e}")

        # 等待指定的时间间隔后进行下一次迭代
        time.sleep(interval)


def calculate_string_md5(text: str) -> str:
    """
    计算并返回指定字符串的MD5哈希值。

    参数:
    text (str): 要计算MD5值的字符串。

    返回值:
    str: 计算得到的MD5哈希值，以十六进制形式表示。
    """
    # 使用UTF-8编码将字符串转换为字节串，因为MD5算法操作的是字节序列
    encoded_text = text.encode('utf-8')

    # 创建一个MD5哈希对象
    md5_hasher = hashlib.md5()

    # 通过update方法将字节串输入到MD5哈希对象中进行计算
    md5_hasher.update(encoded_text)

    # 获取并返回计算完成的MD5哈希值，以十六进制字符串形式
    return md5_hasher.hexdigest()


def get_chat_message(tab: ChromiumTab):
    """
    从ChromiumTab对象中获取聊天消息，并保存到日志文件中。

    参数:
    - tab: ChromiumTab对象，表示一个浏览器标签页，用于交互和获取页面元素。

    返回值:
    无
    """
    logger = log_loader.get_logger()
    logger.debug('开始监听聊天历史')
    redis_client = get_redis_client()

    interval = 2  # 设置监听间隔时间为1秒

    while True:
        # 检查是否停止监听
        listen_state = redis_client.get(LISTEN_STATE)
        if listen_state and listen_state.isdigit() and int(listen_state) == 0:
            logger.debug('聊天历史监听已停止')
            break

        try:
            # 提取页面css文件
            if not redis_client.exists(WEB_CSS):
                links_text = ''
                links = tab.ele('tag:head').eles('tag:link')
                for link in links:
                    if '.css' in link.link:
                        links_text = links_text + '\n' + link.html
                if len(links_text) > 100:
                    redis_client.set(WEB_CSS, links_text, ex=60 * 10)

            # 从页面中获取会话列表
            conversation_list = tab.eles('tag:div@@data-testid^conversation-turn-')
            question = answer = data_message_id = None  # 初始化问题、答案和消息ID

            for conversation in conversation_list:
                # 解析会话中的用户或助手名称和内容
                chat_name = conversation.ele('.font-semibold select-none')
                chat_content = conversation.ele(
                    'tag:div@|data-message-author-role=user@|data-message-author-role=assistant')
                chat_data_message_id = chat_content.attr('data-message-id')
                chat_role = chat_content.attr('data-message-author-role')

                # 根据角色区分问题和答案
                if chat_role == 'user':
                    question = chat_content.text
                    data_message_id = chat_data_message_id
                elif chat_role == 'assistant' and question:
                    # 判断是否已记录过该答案，如果未记录过，则记录答案
                    start_str = redis_client.get(CHAT_RECORDS_START + data_message_id)
                    if start_str is None:
                        # 记录答案，并保存到日志文件中
                        markdown = redis_client.get(ANSWER_MARKDOWN)
                        if markdown is None:
                            answer = chat_content.text
                        elif markdown == "1":
                            answer = chat_content.inner_html
                        else:
                            answer = chat_content.text

                        if "help.openai.com" in answer:
                            # 说明系统出错了。需要刷新下页面
                            # 主动刷新下页面
                            tab.refresh()
                            ret = load_chatgpt.get_page(tab)
                            if ret:
                                check_chatgpt.save_chatgpt_interaction()
                                check_chatgpt.save_new_chat()
                                tab.wait.load_start()
                                # 重置问题、答案和消息ID，准备下一次记录
                                question = answer = data_message_id = None
                                redis_client.set(CHAT_MESSAGE_ANSWER, "访问异常请重新提问")
                            continue

                        # 构造新的聊天记录并尝试保存
                        if question and answer:
                            new_chat_record = {
                                'question': question,
                                'answer': answer,
                                'md5': calculate_string_md5(answer),
                                'data_message_id': data_message_id
                            }
                            # 保存聊天记录至指定文件
                            records_file_name = redis_client.get(CHAT_RECORDS_LAST)
                            chat_instance_from_dict = ChatQuestionAnswer.from_dict(new_chat_record)
                            save_ret = chat_log.add_chat_log(chat_instance_from_dict, records_file_name + ".json")
                            if save_ret:
                                # 成功保存后更新记录时间
                                redis_client.set(CHAT_RECORDS + data_message_id, time.time())
                            else:
                                # 判断是否保存结束
                                start_str = redis_client.get(CHAT_RECORDS_START + data_message_id)
                                if start_str is None:
                                    current_timestamp = time.time()
                                    save_ret_time = float(redis_client.get(CHAT_RECORDS + data_message_id))
                                    # 当前时间-保存时间>间隔时间， 代表当次聊天已经结束，可以投递给请求方了。
                                    # 因为网页会卡，因此有时候还要判断Copy 按钮是否存在
                                    copy_button = conversation.ele('.:text-gray-400')

                                    if current_timestamp - save_ret_time > interval:
                                        # 使用正则表达式匹配独立的 "visible" 类名
                                        if "] visible" in copy_button.attr('class'):
                                            redis_client.set(CHAT_RECORDS_START + data_message_id, records_file_name,
                                                             ex=60 * 10)
                                            redis_client.set(CHAT_MESSAGE_ANSWER, answer)
                                            logger.debug(f"消息：{chat_instance_from_dict.question[:20]}...保存结束")

                            # 重置问题、答案和消息ID，准备下一次记录
                            question = answer = data_message_id = None
                    else:
                        # 只要是从页面上能采集到的都代表当次的聊天，需要一直保持状态，直达页面无记录后，自动释放
                        redis_client.set(CHAT_RECORDS_START + data_message_id, start_str, ex=60 * 10)
        except ElementNotFoundError:
            # 忽略找不到元素的错误，继续监听
            pass

        # 等待间隔时间后继续监听
        time.sleep(interval)
